package com.crimsec.utils.dinfo;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.PrintStream;
import static java.lang.System.*;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

public class App implements IDefaults {

    public static void main(String[] args) {
        if ((args.length > 0) && (args[0].contains("h"))) {
            showusage(out);
            exit(0);
        }

        if (args.length < 4) {
            showusage(err);
            exit(-1);
        }

        try {
            File initial = new File(args[0]);

            IndexMaker im = new IndexMaker();
            im.setInitial(initial);
            im.setNameFilter(args[1]);

            IRenderer renderer = RendererFactory.newRendererInstance(
                    Utils.getInstance(new App(),System.getProperty("im.cfg")));

            renderer.render(im.getNewsFilesMap(), initial, args[2]/*index name*/, Integer.parseInt(args[3]));

        } catch (Exception e) {
            showerror(e, "");
        }

    }

    public static void showerror(Exception e, String msg) {
        err.println("some error occured:\n");
        err.println(msg);
        err.println(e.getMessage());
    }

    public static void showusage(PrintStream ps) {
        ps.println("\nIndex Maker utility");
        ps.println("creates an index file containing the names of last modified files");
        ps.println("\nUsage:\n");
        ps.println("\tjava -cp dinfo-<version>.jar <dirname> <regex> <indexname> <count>");
        ps.println("\nExample:\n");
        ps.println("\tjava -cp dinfo-1.0-SNAPSHOT.jar \"/tmp\" \".txt,.pdf\" index 5");
        ps.println("Will create an file, named index, containing records about the last 5 changed files, in tmp dir");
        ps.println("with extensions pdf and txt\n");
    }

    public String getDefPropsName() {
        return "dinfo.properties";
    }

    public Map<String, String> getDefaults() {
        return new HashMap<String, String>(0);
    }

    public static class IndexMaker implements FileFilter, Comparator {

        private SortedMap<Long, File> sortedFilesMap = null;
        private File initial;
        private String nameFilter = "";

        /**
         * Устанавливает начальный каталог поиска файлов
         *
         * @param initial
         */
        public void setInitial(File initial) {
            clearMap();//использовать как маркер того, что каталог изменился
            this.initial = initial;
        }

        private void clearMap() {
            if (this.sortedFilesMap != null) {
                sortedFilesMap.clear();
                sortedFilesMap = null;
            }
        }

        /**
         * Устанавливает маску поиска файлов в каталоге initial TODO:в
         * настроящий момент, поиск идет без учета регулярных выражений по
         * методу String.endsWith(nameFilter)
         *
         * @param nameFilter маска поиска
         *
         */
        public void setNameFilter(String nameFilter) {
            clearMap();//использовать как маркер того, что каталог изменился
            this.nameFilter = nameFilter;
        }

        public String getNameFilter() {
            return nameFilter;
        }

        public File getInitial() {
            return initial;
        }

        public IndexMaker() {
        }

        /**
         * Возвращает сортированную, в обатном порядке, карту файлов найденных в
         * каталоге initial, ключем карты являтеся дата изменения файла,
         * значением - файл
         *
         * @return SortedMap
         * @throws IOException
         * @see #getFiles()
         */
        public SortedMap<Long, File> getNewsFilesMap() throws IOException {
            if (sortedFilesMap != null) {
                return sortedFilesMap;
            } else {
                getFiles();
                return sortedFilesMap;
            }

        }

        /**
         * Возвращает список файлов в начальном каталоге initial согласно маске
         * nameFilter . Для ускорения работы последующие вызовы метода, без
         * изменения initial или nameFilter, возвращают прежний список без
         * перечитывания файловой системы зановово
         *
         * @return List файлов в каталоге initial
         * @throws IOException
         * @see #setInitial(java.io.File)
         * @see #setNameFilter(java.lang.String)
         *
         */
        public Collection<File> getFiles() throws IOException {
            if ((this.initial != null)
                    && (this.initial.isDirectory())) {
                if (this.sortedFilesMap != null) { //
                    return sortedFilesMap.values();
                } else {//
                    this.sortedFilesMap = new TreeMap<Long, File>(this);
                    initial.listFiles(this); //неявное наполнение this.sortedFilesMap
                    return sortedFilesMap.values();
                }
            } else {
                throw new IOException("intital must be a dir");
            }

        }

        @Override
        public boolean accept(File pathname) {
            //    System.out.println("before:"+pathname.getName());
            String[] masks = nameFilter.split(",");
            if (pathname.isFile()) {
                String name = pathname.getName();
                for (int i = 0; i < masks.length; i++) {
                    if (name.endsWith(masks[i])) {
                        this.sortedFilesMap.put(pathname.lastModified(), pathname);
                        return true;
                    }
                }
                return false;
            } else {
                return false;
            }
        }

        /**
         * инвертирует сортировку Long которая, из ряда по возрастанию
         * 1,2,3,...max инвертируется в max...3,2,1
         *
         * @param o1
         * @param o2
         * @return
         */
        @Override
        public int compare(Object o1, Object o2) {
            //   Long.
            //     return -(Long.compare((Long) o1, (Long) o2)); //since jdk-1.7
            return -(((Long) o1).compareTo((Long) o2));
        }
    }
}
